#include <cassert>
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
#include <array>
#include <vector>
using namespace std;

#include <boost/mp11.hpp>
using namespace boost::mp11;

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/mpl.hpp>

namespace bzbee
{
    // 获取对象成员变量名称
    template<class Seq>
    constexpr std::array<const char*, boost::fusion::result_of::size<Seq>::value> get_member_names_array()
    {
        std::array<const char*, boost::fusion::result_of::size<Seq>::value> members{};
        boost::mp11::mp_for_each<boost::mp11::mp_iota_c<boost::fusion::result_of::size<Seq>::value>>([&members](auto I) {
            members[I] = boost::fusion::extension::struct_member_name<Seq, I>::call();
            });
        return members;
    }

    template<class Seq>
    constexpr std::vector<const char*> get_member_names()
    {
        std::vector<const char*> members;
        boost::mp11::mp_for_each<boost::mp11::mp_iota_c<boost::fusion::result_of::size<Seq>::value>>([&members](auto I) {
            members.emplace_back(boost::fusion::extension::struct_member_name<Seq, I>::call());
            });
        return members;
    }
};

#define BZBEE_REFLECTION_DECLARE() \
public: \
    static const char* get_member_name(int index) \
    { \
        if (index < 0 || index >= s_member_number) \
        { \
            return nullptr; \
        } \
        return s_member_names[index]; \
    } \
private: \
    static const std::vector<const char*> s_member_names; \
    static const size_t s_member_number;

#define BZBEE_REFLECTION(...) BOOST_FUSION_ADAPT_STRUCT(__VA_ARGS__);

#define BZBEE_REFLECTION_DEFINE(s) \
const std::vector<const char*> s::s_member_names = bzbee::get_member_names<s>(); \
const size_t s::s_member_number = s_member_names.size();

struct Parent
{
    int i;
    int32_t i32;
    uint32_t ui32;
    int64_t i64;
    uint64_t ui64;
    std::string str;

    BZBEE_REFLECTION_DECLARE();
};

BZBEE_REFLECTION(Parent, i, i32, ui32, i64, ui64, str);

BZBEE_REFLECTION_DEFINE(Parent);

int main(int argc, char* argv[])
{
    {
        using L = std::tuple<int, char, float, double>;
        using LSize = mp_size<L>;
        cout << LSize::value << endl;
    }
    {
        using L1 = std::tuple<void, int, float>;
        using L2 = mp_transform<std::add_pointer_t, L1>;
        static_assert(std::is_same<L2, std::tuple<void*, int*, float*>>::value, "mp_transform failed");
    }
    {
        using L1 = std::tuple<void, int, double>;
        using L2 = mp_list<void, int, float>;
        //using R1 = mp_all<mp_transform<std::is_same, L1, L2>>; // compile-time error, why???
    }
    {
        using R1 = mp_and<mp_true, mp_true>;   // mp_true
        using R2 = mp_and<mp_false, void>;     // mp_false, void is not reached
        using R3 = mp_and<mp_false, mp_false>; // mp_false
        using R4 = mp_and<void, mp_true>;      // mp_false (!)
    }
    {
        using R1 = mp_all<mp_true, mp_true>;   // mp_true
        //using R2 = mp_all<mp_false, void>;     // compile-time error
        using R3 = mp_all<mp_false, mp_false>; // mp_false
        //using R4 = mp_all<void, mp_true>;      // compile-time error
    }

    {
        const auto& names = bzbee::get_member_names<Parent>();
        cout << &names[0] << endl;
    }
    {
        const auto& names = bzbee::get_member_names<Parent>();
        cout << &names[0] << endl;
    }
    {
        cout << Parent::get_member_name(0) << endl;
    }
    {
        cout << Parent::get_member_name(0) << endl;
    }
    {
        Parent p;
        auto& [i, i32, ui32, i64, ui64, str] = p;
        i = 1;
        cout << p.i << endl;
    }

    return 0;
}
